#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "const.h"
#include "./crypt/EmAES.h"
#include "StdAfx.h"
#include "./PICData/HexFileLine.h"

#define LOG_HEX(des,val)  LogOut(des); LogHex(val); LogOut("\n");
#define LOG_INT(des,val)  LogOut(des); LogInt(val); LogOut("\n");

#define OUT_SUFFIX      ".img"
#define RAW_SUFFIX      ".bin"
#define SN_SUFFIX       ".bin"
#define SN_LENGTH       8    // not include the "LD" or "CA" prefix

#define  LD_START_ADDR  0x00100000
#define  AP_START_ADDR  0x00000000

#define  LD_ROM_MAX     (4096-512)

#define  VALID_FLAG             (0x00abcdef)
typedef unsigned long uint32_t;
typedef unsigned short uint16_t;
typedef unsigned char uint8_t;
typedef struct tagDevParam
{
	uint32_t   flag;   	// must be VALID_FLAG
	uint16_t   sn[10];    // serial number
	uint8_t    key[32]; // aes key table
}DevParam_t,*PDevParam_t;

typedef enum
{
    OM_ld,
    OM_ap,
    OM_sn,
    OM_default,
}OperateMode;

typedef struct tagISPMaker
{
    OperateMode mode;
    unsigned long startAddress;
    TCHAR sn[16];
    TCHAR inputFile[1024];
    TCHAR outputFile[1024];
    TCHAR snFile[1024];
	TCHAR outputRawFile[1024];
}ISPMaker_t,*PISPMaker_t;

ISPMaker_t  ispMaker = {OM_default};

bool LoadFile(LPCTSTR fileName, unsigned long startAddress, LPCTSTR destFile);
int LoadROMForBinary(LPCTSTR fileName, unsigned long startAddress, LPCTSTR destFile);
int LoadROMForHex(LPCTSTR fileName, unsigned long* startAddress, LPCTSTR destFile);
bool  CreateSN(LPCTSTR snFileName, LPCTSTR sn);
bool  LoadSN(LPCTSTR snFileName, LPCTSTR sn);
bool  CheckSN(LPCTSTR snFileName, LPCTSTR sn);
bool  SIMDownLoad(LPCTSTR fileName);

static void Usage()
{
    printf("ispcrypt <inputfilename> <outputfilename> [start address in hex format]\n");
    printf("inputfilename support both binary and hex file format\n");
    printf("\n");
    printf("to create a bootloader image, use:\n"
           "  ispcrypt ld <inputfile> <SN> [-o] [-s] [-a]\n");
    printf("\n");
    printf("to create a application image, use:\n"
           "  ispcrypt ap <inputfile> <SN> [-o] [-s] [-a]\n");
    printf("\n");
    printf("to create a serial number, use:\n"
           "  ispcrypt sn <SN> [-s]\n");
    printf("\n");
    printf("Options\n");
    printf(
        "  -o<fileName> output file name,  For ld command, default is <SN>.ld.img.\n"
        "                                  For ap command, default is <SN>.ap.img\n"
        "  -s<SN Path> serial number path, For ld and ap command, specify the serial\n"
        "                                  number search path.\n"
        "                                  For sn command, specify the sn file create\n"
        "                                  path. sn file name is <SN>.bin\n"
        "                                  If SN path not specified, use current work\n"
        "                                  directory\n"
        "  -a<address> image start address,For ld command, default is 0x00100000.\n"
        "                                  For ap command, default is 0x00000000\n"
    );
    exit(-2);
}

static void LogOut(LPCTSTR str)
{
    printf("%s",str);
}

static void LogHex(unsigned long value)
{
    printf("0x%08X",(unsigned int)value);
}

static void LogInt(unsigned long value)
{
    printf("%d",(unsigned int)value);
}


static unsigned long simple_strtoul(const char *cp,char **endp,unsigned int base)
{
	unsigned long result = 0,value;

	if (*cp == '0') {
		cp++;
		if ((*cp == 'x') && isxdigit(cp[1])) {
			base = 16;
			cp++;
		}
		if (!base) {
			base = 8;
		}
	}
	if (!base) {
		base = 10;
	}
	while (isxdigit(*cp) && (value = isdigit(*cp) ? *cp-'0' : (islower(*cp)
	    ? toupper(*cp) : *cp)-'A'+10) < base) {
		result = result*base + value;
		cp++;
	}
	if (endp)
		*endp = (char *)cp;
	return result;
}

static long simple_strtol(const char *cp,char **endp,unsigned int base)
{
	if(*cp=='-')
		return -(long)simple_strtoul(cp+1,endp,base);
	return simple_strtoul(cp,endp,base);
}

static int ParseOption(int argc, char* argv[], int start)
{
        for(int i=start; i<argc;i++){
            if(argv[i][0] == '-'){
                switch(argv[i][1]){
                    case 'o':
                    case 'O':
                        {
                            strcpy(ispMaker.outputFile,argv[i]+2);
                            break;
                        }
                    case 's':
                    case 'S':
                        {
                            char snPath[1024] = {0};
                            strcpy(snPath,argv[i]+2);
                            if(snPath[strlen(snPath)-1] != '/' || snPath[strlen(snPath)-1] != '\\'){
                                strcat(snPath,"/");
                            }
                            strcat(snPath,ispMaker.snFile);
                            strcpy(ispMaker.snFile,snPath);
                        }
                        break;
                    case 'a':
                    case 'A':
                        {
                            unsigned long addr;
                            unsigned long startAddress = simple_strtoul(argv[3],0,16);
                            addr = startAddress & (~3);
                            if(addr != startAddress){
                                startAddress = addr;
                                LOG_HEX(_T("start address is aligned to "),startAddress);
                            }
                            ispMaker.startAddress = startAddress;
                            break;
                        }
                }
            }
        }
        return 0;
}

static void PrintMode(void)
{
    switch (ispMaker.mode){
        case OM_ld:
            LogOut(_T("Run in loader create mode\n"));
            LogOut(_T("Input file: \""));
            LogOut(ispMaker.inputFile);
            LogOut(_T("\"\n"));
            LogOut(_T("Output file: \""));
            LogOut(ispMaker.outputFile);
            LogOut(_T("\"\n"));
            LogOut(_T("SN file: \""));
            LogOut(ispMaker.snFile);
            LogOut(_T("\"\n"));
            LOG_HEX(_T("Start address: "),ispMaker.startAddress);
            return;
        case OM_ap:
            LogOut(_T("Run in app create mode\n"));
            LogOut(_T("Input file: \""));
            LogOut(ispMaker.inputFile);
            LogOut(_T("\"\n"));
            LogOut(_T("Output file: \""));
            LogOut(ispMaker.outputFile);
            LogOut(_T("\"\n"));
            LogOut(_T("SN file: \""));
            LogOut(ispMaker.snFile);
            LogOut(_T("\"\n"));
            LOG_HEX(_T("Start address: "),ispMaker.startAddress);
            return;
        case OM_sn:
            LogOut(_T("Run in SN create mode\n"));
            LogOut(_T("SN file: \""));
            LogOut(ispMaker.snFile);
            LogOut(_T("\"\n"));
            LOG_HEX(_T("Start address: "),ispMaker.startAddress);
            return;
        case OM_default:
        default:
            LogOut(_T("Run in default mode\n"));
            return;
    }
}

static int ParseArg(int argc, char* argv[])
{
    if(argc < 3) Usage();
    if(strcmp(argv[1],"ld") == 0){
        if(argc<4) Usage();
        //LogOut(_T("Run in loader create mode\n"));
        ispMaker.mode = OM_ld;
        strcpy(ispMaker.inputFile,argv[2]);
        strcpy(ispMaker.sn,argv[3]);
        if(strlen(ispMaker.sn)>SN_LENGTH){
            ispMaker.sn[SN_LENGTH] = 0;
        }
        // extend sn to SN_LENGTH
        while(strlen(ispMaker.sn)<SN_LENGTH){
            memmove(ispMaker.sn+1,ispMaker.sn,SN_LENGTH);
            ispMaker.sn[0] = '0';
        }

        strcpy(ispMaker.snFile,ispMaker.sn);
        strcat(ispMaker.snFile,SN_SUFFIX);

        strcpy(ispMaker.outputFile,ispMaker.sn);
        strcat(ispMaker.outputFile,".ld");
        strcat(ispMaker.outputFile,OUT_SUFFIX);
		strcpy(ispMaker.outputRawFile,ispMaker.sn);
        strcat(ispMaker.outputRawFile,".ld");
        strcat(ispMaker.outputRawFile,RAW_SUFFIX);
        ispMaker.startAddress = LD_START_ADDR;
        ParseOption(argc,argv,4);
    }else if(strcmp(argv[1],"ap") == 0){
        if(argc<4) Usage();
        //LogOut(_T("Run in app create mode\n"));
        ispMaker.mode = OM_ap;
        strcpy(ispMaker.inputFile,argv[2]);
        strcpy(ispMaker.sn,argv[3]);
        if(strlen(ispMaker.sn)>SN_LENGTH){
            ispMaker.sn[SN_LENGTH] = 0;
        }
        // extend sn to SN_LENGTH
        while(strlen(ispMaker.sn)<SN_LENGTH){
            memmove(ispMaker.sn+1,ispMaker.sn,SN_LENGTH);
            ispMaker.sn[0] = '0';
        }

        strcpy(ispMaker.snFile,ispMaker.sn);
        strcat(ispMaker.snFile,SN_SUFFIX);

        strcpy(ispMaker.outputFile,ispMaker.sn);
        strcat(ispMaker.outputFile,".ap");
        strcat(ispMaker.outputFile,OUT_SUFFIX);
        ispMaker.startAddress = AP_START_ADDR;
        ParseOption(argc,argv,4);
    }else if(strcmp(argv[1],"sn") == 0){
        //LogOut(_T("Run in SN create mode\n"));
        ispMaker.mode = OM_sn;
        strcpy(ispMaker.sn,argv[2]);
        if(strlen(ispMaker.sn)>SN_LENGTH){
            ispMaker.sn[SN_LENGTH] = 0;
        }
        // extend sn to SN_LENGTH
        while(strlen(ispMaker.sn)<SN_LENGTH){
            memmove(ispMaker.sn+1,ispMaker.sn,SN_LENGTH);
            ispMaker.sn[0] = '0';
        }
        strcpy(ispMaker.snFile,ispMaker.sn);
        strcat(ispMaker.snFile,SN_SUFFIX);
        
        ispMaker.outputFile[0] = 0;
        ispMaker.inputFile[0] = 0;
        ParseOption(argc,argv,3);
    }else{
        //LogOut(_T("Run in default mode\n"));
        ispMaker.mode = OM_default;
    }
    return 0;
}

/**
  new command line :
  // Make bootloader image, if the serial number not exist, will create one
  ispcrypt ld <ld binary or hex > <serial number(8 characters)> [-o<oupput filename, default sn.ld.img>] [-s<SN file path>] [-a<Start address. default 0x00100000>]
  // make application image, the serial number must exist
  ispcrypt ap <app binary or hex> <serial number(8 characters)> [-o<oupput filename, default sn.ap.img>] [-s<SN file path>] [-a<Start address, default 0x00000000>]
  // make a new serial number, this will create a serial number file under specify path
  ispcrypt sn <serial number>  [-s<SN file path>]
 */

int main(int argc, char* argv[])
{
    unsigned long startAddress = 0;
    int res = ParseArg(argc,argv);
    PrintMode();
    switch(ispMaker.mode){
        case OM_ld:
        case OM_ap:
            if(LoadFile(ispMaker.inputFile,ispMaker.startAddress,ispMaker.outputFile)){
                return 0;
            }
            return -1;
            break;
        case OM_sn:
            if(CreateSN(ispMaker.snFile,ispMaker.sn)){
                return 0;
            }
            return -1;
        break;
        case OM_default:
        default:
        break;
    }

    if(argc<3){
        Usage();
    }
    if(argc>4){
        if(SIMDownLoad(argv[1])){
            LogOut(_T("simulate download success!\n"));
        }else{
            LogOut(_T("simulate download fail!\n"));
        }
        return 0;
    }
    if(argc == 4){
        unsigned long addr;
        startAddress = simple_strtoul(argv[3],0,16);
        addr = startAddress & (~3);
        if(addr != startAddress){
            startAddress = addr;
            LOG_HEX(_T("start address is aligned to "),startAddress);
        }
    }
    if(argc>1){
         LoadFile(argv[1],startAddress,argv[2]);
    }
    return 0;
}


static LPCTSTR GetSuffix(LPCTSTR fileName)
{
    if(fileName && fileName[0])
    {
        LPCTSTR suffix = NULL;
        while(*fileName){
            if(*fileName == _T('.')){
                suffix = fileName;
            }
            fileName++;
        }
        if(suffix){
            return suffix+1;
        }
    }
    return NULL;
}

ProgBlock_t  blocks[MAX_BLOCK_CNT];
unsigned char rom[MAX_ROM_SIZE];
bool CryptBlock(unsigned long startAddress, int length, int blockCount, int crcValue, LPCTSTR destFile);
int CreateBlock(unsigned long startAddress, const unsigned char* data, unsigned long len);

bool LoadFile(LPCTSTR fileName, unsigned long startAddress, LPCTSTR destFile)
{
    bool res = false;
    LPCTSTR suffix = GetSuffix(fileName);
    int length;
    unsigned long crcValue = 0;
    int blockCount = 0;
    if(!fileName)return false;
    if(!fileName[0])return false;
    LogOut(_T("Suffix is: "));
    LogOut(suffix);
    LogOut(_T("\n"));
    memset(rom,0xff,sizeof(rom));
    if(   suffix
      && (suffix[0] == _T('h') || suffix[0] == _T('H'))
      && (suffix[1] == _T('e') || suffix[1] == _T('E'))
      && (suffix[2] == _T('x') || suffix[2] == _T('X'))
    ){
          LogOut(_T("Load for hex file!\n"));
          length = LoadROMForHex(fileName,&startAddress,destFile);
    }else{
          LogOut(_T("Load for binary file!\n"));
          LogOut(_T("Start Address: "));
          LogHex(startAddress);
          LogOut(_T("\n"));
          length = LoadROMForBinary(fileName,startAddress,destFile);
    }
    if(!length){
        LogOut(_T("Load file \""));
        LogOut(fileName);
        LogOut(_T("\" fail\n"));
        return res;
    }

    if(ispMaker.mode == OM_ld){
        if(!CheckSN(ispMaker.snFile,ispMaker.sn)){
            if(!CreateSN(ispMaker.snFile,ispMaker.sn)){
                if(!CheckSN(ispMaker.snFile,ispMaker.sn))return res;
            }
        }
        if(length > LD_ROM_MAX){
            LogOut(_T("Loader image size is too large to place into ldrom!\n"));
            return res;
        }
        FILE* fp = _tfopen(ispMaker.snFile,"rb");
        if(!fp)return res;
        DevParam_t dev;
        size_t rlen = fread(&dev,1,sizeof(dev),fp);
        fclose(fp);
        if(rlen != sizeof(dev))return res;
        memcpy(rom+LD_ROM_MAX,&dev,sizeof(dev));
        length = LD_ROM_MAX + sizeof(dev);
		{
			// generate raw ld image
			FILE* fp = _tfopen(ispMaker.outputRawFile,"wb+");
			int wrLen = (int)fwrite(rom,1,length,fp);
			if(wrLen != length){
				LogOut(_T("Fail to generate out raw file\n"));
			}else{
				LogOut(_T("Output raw bootloader image:"));
				LogOut(ispMaker.outputRawFile);
				LogOut(_T("\n"));
			}
			fclose(fp);
		}
    }else if(ispMaker.mode == OM_ap){
        if(!LoadSN(ispMaker.snFile,ispMaker.sn)){
            return res;
        }
    }

    LogOut(_T("File length is: "));
    LogInt(length);
    LogOut(_T("\n"));
    // Prepare to calculate crc value
    MakeCrc32Table();
    crcValue = CalcCrc32(rom,length);

    LogOut(_T("ROM CRC value is: "));
    LogHex(crcValue);
    LogOut(_T("\n"));    

    blockCount = CreateBlock(startAddress,rom,length);
    
    res = CryptBlock(startAddress,length,blockCount,crcValue,destFile);
    if(!res){
        LogOut(_T("Fail to crpyt file!\n"));
    }
    return res;
}

int CreateBlock(unsigned long startAddress, const unsigned char* data, unsigned long len)
{
    int blockCount = 0;
    int mixBlockCount = 0;
    int mixCount;
    /* fix block with rom data */
    while(len){
        int cpyLen = 60;
        if(len < 60){
            cpyLen = len;
            len = 0;
        }else{
            len -= 60;
        }
        blocks[blockCount].addr = startAddress;
        memset(blocks[blockCount].data,0xff,60);
        memcpy(blocks[blockCount].data,data,cpyLen);
        data += cpyLen;
        startAddress += cpyLen;
        blockCount++;
    }
    /* fix block with random data */
    srand((unsigned int)time(0));
    if(1){
        mixBlockCount = (rand()%(MIX_MAX_SIZE - MIX_MIN_SIZE)) + MIX_MIN_SIZE;
        while(mixBlockCount){
            unsigned char * p = (unsigned char*)&blocks[blockCount];
            int i = 0;
            for(i=0;i<sizeof(blocks[0]);i++){
                *p++ = (unsigned char)rand();
            }
            blocks[blockCount].addr &= 0x0fffffff; // make sure the address is no 0xffffffff
            blocks[blockCount].addr |= 0x80000000; // make sure the address is out of range
            mixBlockCount--;
            blockCount++;
        }
    }

    /* mix the blocks */
    if(1){
        mixCount = 5000; // repreat about 5000 times
        while(mixCount--){
            int a = rand() % blockCount;
            int b = rand() % blockCount;
            // swap a and b
            ProgBlock_t tmp;
            memcpy(&tmp,&blocks[a],sizeof(blocks[0]));
            memcpy(&blocks[a],&blocks[b],sizeof(blocks[0]));
            memcpy(&blocks[b],&tmp,sizeof(blocks[0]));
        }
    }
    
    /* add a terminate blcok */
    {
        unsigned char * p = (unsigned char*)&blocks[blockCount];
        int i = 0;
        for(i=0;i<sizeof(blocks[0]);i++){
            *p++ = (unsigned char)rand();
        }
        blocks[blockCount].addr = 0xffffffff;
        blockCount++;
    }

    return blockCount;
}

bool CryptBlock(unsigned long startAddress, int length, int blockCount, int crcValue, LPCTSTR destFile)
{
    unsigned char cipher[16];
    unsigned long rawData[16];
    InitBlock_t&  initBlock = *(PInitBlock_t)rawData;
    int i = 0;
    FILE* fp = _tfopen(destFile,"wb+");
    if(!fp){
        LogOut(_T("Fail to open \""));
        LogOut(destFile);
        LogOut(_T("\" for output!\n"));
        return false;
    }
    for(i=0;i<64;i++){
        //rawData[i] = (unsigned long)rand();
        initBlock.cipher[i] = (unsigned char)rand();
    }
    memcpy(cipher,initBlock.cipher,16);
    initBlock.startAddress = startAddress;
    initBlock.crc = crcValue;
    initBlock.length = length;
    initBlock.blockCount = blockCount;

    aesEncInit();
    aesEncrypt((unsigned char*)(rawData+4),cipher);
    aesEncrypt((unsigned char*)(rawData+8),cipher);
    aesEncrypt((unsigned char*)(rawData+12),cipher);
    fwrite(rawData,1,64,fp);
    
    for(i=0;i<blockCount;i++){
        if(0){
            static FILE* flog = NULL;
            if(!flog){
                flog = fopen("xxx.log","w+");
                fprintf(flog,"Address list:\n");
            }
            fprintf(flog,"0x%08X\n",(unsigned int)blocks[i].addr);
        }
        memcpy(rawData,&blocks[i],sizeof(blocks[0]));
        aesEncrypt((unsigned char*)(rawData+0),cipher);
        aesEncrypt((unsigned char*)(rawData+4),cipher);
        aesEncrypt((unsigned char*)(rawData+8),cipher);
        aesEncrypt((unsigned char*)(rawData+12),cipher);
        fwrite(rawData,1,64,fp);
    }
    fclose(fp);
    LogOut(_T("Success to create isp file \""));
    LogOut(destFile);
    LogOut(_T("\"\n"));
    LogOut(_T("File size: "));
    LogInt(blockCount*64+64);
    LogOut(_T("\n"));
    return true;
}

int LoadROMForBinary(LPCTSTR fileName, unsigned long startAddress, LPCTSTR destFile)
{
    FILE* fp = _tfopen(fileName,_T("rb"));
    int length = 0;
    int readLen = 0;
    unsigned long crcValue = 0;
    bool res = false;
    if(!fp)return false;
    fseek(fp,0,SEEK_END);
    length = ftell(fp);
    if(length > MAX_ROM_SIZE){
        res = false;
        LogOut(_T("File size is too large!\n"));
        goto end;
    }
    fseek(fp,0,SEEK_SET);
    readLen = (int)fread(rom,1,length,fp);
    if(readLen != length){
        res = false;
        LogOut(_T("Read file fail!\n"));
        goto end;
    }
    // Padding with 0xff
    while(length & 3){
        rom[length++] = 0xff;
    }

    res = true;
end:
    if(fp){
        fclose(fp);
    }
    if(!res){
        return 0;
    }
    return length;
}

#define FLASH_BASE         0

int LoadROMForHex(LPCTSTR fileName, unsigned long* startAddress, LPCTSTR destFile)
{
    int i = 0;
    unsigned long linearAddress = 0;
    unsigned long length = 0;
    *startAddress = 0xffffffff;
    CHexFile hexFile(fileName);
    //LOG_INT(_T("Hex line cout is:"),hexFile.LineCount);
    for(i=0;i<hexFile.LineCount;i++){
        CHexFileLine& line = hexFile.Lines[i];
        //LOG_INT(_T("Data Type = "),line.RecordType);
        //LOG_HEX(_T("Data length = "),line.RecordNumber);
		switch(line.RecordType){
			case t_Linear:
				linearAddress = 
			(((unsigned long)line.Records[0])<<24) + 
			(((unsigned long)line.Records[1])<<16);
                //LOG_INT(_T("linearAddress = "),linearAddress);
				break;
			case t_Data:
                if(*startAddress > line.RecordAddress + linearAddress){
                    *startAddress = line.RecordAddress + linearAddress;
                }
				if(line.RecordAddress + linearAddress >= FLASH_BASE){
					unsigned long addr = line.RecordAddress + linearAddress - FLASH_BASE;
					if(addr<MAX_ROM_SIZE){
                        //for(int i=0;i<line.RecordNumber;i++){
                        //    if(addr+i+FLASH_BASE>=0x08003E00 && addr+i+FLASH_BASE<AppBaseAddr){
                        //    }else{
                        //        STM32_Flash[addr+i] = line.Records[i];
                        //    }
                        //}
						memcpy(&rom[addr],
						line.Records,
						line.RecordNumber );
						if(length < addr+line.RecordNumber-1+FLASH_BASE){
							length = addr+line.RecordNumber-1+FLASH_BASE;
						}
					}
                    
                    //LOG_HEX(_T("Data Address = "),addr);
                    //LOG_HEX(_T("Data length = "),line.RecordNumber);
				}
				break;
            case t_EOF:
                i = hexFile.LineCount;
                break;
			default:
				break;
		}      
    }
    length+=1;
    length -= *startAddress;
    memmove(rom,rom+*startAddress,length);
    return length;
}


#define  PAGE_SIZE   (512)
#define  PAGE_MASK    (~511)
// bitmap for page usage
unsigned long pageUsage[MAX_ROM_SIZE/PAGE_SIZE/32];

void ProgramData(unsigned long addr, const unsigned long* data,unsigned long len)
{
    unsigned long row = addr/PAGE_SIZE;
    unsigned long bit = 1<<(row&31);
    unsigned char* p = (unsigned char*)data;
    row /= 32;
    if( !(pageUsage[row]&bit) ){
        pageUsage[row] |= bit;
        memset( rom + (addr & PAGE_MASK),0xff,PAGE_SIZE);
    }
    while(len){
        if(rom[addr] == 0xff){
            rom[addr++] = *p++;
        }
        len--;
    }
}

int gInit = 0;
int g_blockCount;
unsigned long g_length;
unsigned long g_crcValue;
unsigned long g_startAddress;
void ProgramROM(const unsigned long* data)
{
    static unsigned char cipher[16];
    unsigned long rawData[16];
    memcpy(rawData,data,64);
    if(gInit == 1){
        InitBlock_t& iniBlock = *(PInitBlock_t)rawData;
        gInit = 0;
        aesDecInit();
        memcpy(cipher,rawData,16);
        memset(pageUsage,0,sizeof(pageUsage));
        aesDecrypt((unsigned char*)(rawData+4),cipher);
        aesDecrypt((unsigned char*)(rawData+8),cipher);
        aesDecrypt((unsigned char*)(rawData+12),cipher);
        g_blockCount = iniBlock.blockCount;
        g_length = iniBlock.length;
        g_crcValue = iniBlock.crc;
        g_startAddress = iniBlock.startAddress;
        //LOG_HEX(_T("Crc value is: "),g_crcValue);
        //LOG_INT(_T("File length is: "),g_length);
    }else if(gInit!=-1){
        ProgBlock_t& block = *(PProgBlock_t)rawData;
        aesDecrypt((unsigned char*)(rawData+0),cipher);
        aesDecrypt((unsigned char*)(rawData+4),cipher);
        aesDecrypt((unsigned char*)(rawData+8),cipher);
        aesDecrypt((unsigned char*)(rawData+12),cipher);

        if(0){
            static FILE* flog = NULL;
            if(!flog){
                flog = fopen("xxx2.log","w+");
                fprintf(flog,"Address list:\n");
            }
            fprintf(flog,"0x%08X\n",(unsigned int)block.addr);
        }

        if(block.addr == 0xFFFFFFFF){
            gInit = -1;
            return;
        }
        if(block.addr > MAX_ROM_SIZE){
            // ignore
        }else{
#if 1
            unsigned remain = (block.addr & PAGE_MASK) + PAGE_SIZE - block.addr;
            if(remain < 60){
                ProgramData(block.addr,block.data,remain);
                ProgramData(block.addr+remain,block.data+(remain>>2),60-remain);
            }else{
                ProgramData(block.addr,block.data,60);
            }
#else
            memcpy(rom+block.addr,block.data,60);
#endif
        }
        g_blockCount--;
    }
}

bool  SIMDownLoad(LPCTSTR fileName)
{
    FILE* fp = fopen(fileName,"rb");
    unsigned long crc;
    memset(rom,0,sizeof(rom));
    gInit = 1;
    LogOut(_T("Simulate Program started ...\n"));
    if(!fp){
        LogOut(_T("Fail to open crpyt file\""));
        LogOut(fileName);
        LogOut(_T("\"\n"));
        return false;
    }
    while(!feof(fp)){
        unsigned long buf[16];
        fread(buf,1,64,fp);
        ProgramROM(buf);
    }
    LogOut(_T("Simulate Program finished\n"));
    LOG_INT(_T("Remain block count is: "),g_blockCount);
    MakeCrc32Table();
    crc = CalcCrc32(rom+g_startAddress,g_length);
    LOG_HEX(_T("Start address is: "),g_startAddress);
    LOG_INT(_T("ROM size is: "),g_length);
    LOG_HEX(_T("Current crc value is: "),crc);
    LOG_HEX(_T("Old crc value is: "),g_crcValue);
    return crc == g_crcValue;
}

bool  CreateSN(LPCTSTR snFileName, LPCTSTR sn)
{
    bool res = false;
    int i = 0;
    size_t len = 0;
    FILE* fp = _tfopen(snFileName,_T("rb"));
    if(fp){
        LogOut(_T("SN File \""));
        LogOut(snFileName);
        LogOut(_T("\" already exist, overwrite it!\n"));
        fclose(fp);
    }
    fp = _tfopen(snFileName,_T("wb+"));
    if(!fp){
        LogOut(_T("Fail to create SN File \""));
        LogOut(snFileName);
        LogOut(_T("\" \n"));
        return res;
    }

    DevParam_t dev = {VALID_FLAG};
    dev.sn[0] = 'L';
    dev.sn[1] = 'D';
    for(i=2;i<10;i++){
        dev.sn[i] = sn[i-2];
    }
    srand((unsigned int)time(0));
    for(i=0;i<sizeof(dev.key);i++){
        dev.key[i] = (uint8_t)rand();
    }
    len = fwrite(&dev,1,sizeof(dev),fp);
    fclose(fp);
    if(len != sizeof(dev)){
        LogOut(_T("Fail to write SN File \""));
        LogOut(snFileName);
        LogOut(_T("\" \n"));
        return res;
    }
    LogOut(_T("Success to create SN File \""));
    LogOut(snFileName);
    LogOut(_T("\" \n"));
    return true;
}

bool  CheckSN(LPCTSTR snFileName, LPCTSTR sn)
{
    bool res = false;
    FILE* fp = _tfopen(snFileName,_T("rb"));
    size_t len;
    int i = 0;
    if(!fp){
        LogOut(_T("Fail to open SN File \""));
        LogOut(snFileName);
        LogOut(_T("\" \n"));
        return res;
    }
    DevParam_t dev;
    len = fread(&dev,1,sizeof(dev),fp);
    fclose(fp);
    if(len != sizeof(dev)){
        LogOut(_T("Fail to read sn file \""));
        LogOut(snFileName);
        LogOut(_T("\" \n"));
        return res;
    }
    if(dev.flag != VALID_FLAG){
        LogOut(_T("Flag mismatch in sn file \""));
        LogOut(snFileName);
        LogOut(_T("\" \n"));
        return res;
    }
    TCHAR SN1[128] = _T("LD");
    TCHAR SN2[128] = {0};
    _tcscat(SN1,sn);
    for(i=0;i<SN_LENGTH+2;i++){
        SN2[i] = (TCHAR)dev.sn[i];
    }
    if(_tcscmp(SN1,SN2)!=0){
        LogOut(_T("SN mismatch in sn file \""));
        LogOut(snFileName);
        LogOut(_T("\" \n"));
        return res;
    }
    
    LogOut(_T("Success to check SN File \""));
    LogOut(snFileName);
    LogOut(_T("\" \n"));
    return true;
}

bool  LoadSN(LPCTSTR snFileName, LPCTSTR sn)
{
    bool res = false;
    FILE* fp = _tfopen(snFileName,_T("rb"));
    size_t len;
    int i = 0;
    if(!fp){
        LogOut(_T("Fail to open SN File \""));
        LogOut(snFileName);
        LogOut(_T("\" \n"));
        return res;
    }
    DevParam_t dev;
    len = fread(&dev,1,sizeof(dev),fp);
    fclose(fp);
    if(len != sizeof(dev)){
        LogOut(_T("Fail to read sn file \""));
        LogOut(snFileName);
        LogOut(_T("\" \n"));
        return res;
    }
    if(dev.flag != VALID_FLAG){
        LogOut(_T("Flag mismatch in sn file \""));
        LogOut(snFileName);
        LogOut(_T("\" \n"));
        return res;
    }
    TCHAR SN1[128] = _T("LD");
    TCHAR SN2[128] = {0};
    _tcscat(SN1,sn);
    for(i=0;i<SN_LENGTH+2;i++){
        SN2[i] = (TCHAR)dev.sn[i];
    }
    if(_tcscmp(SN1,SN2)!=0){
        LogOut(_T("SN mismatch in sn file \""));
        LogOut(snFileName);
        LogOut(_T("\" \n"));
        return res;
    }
    // Load key table from the SN file
    for(i=0;i<sizeof(kTable);i++){
        kTable[i] = dev.key[i];
    }  
    
    LogOut(_T("Success to load SN File \""));
    LogOut(snFileName);
    LogOut(_T("\" \n"));
    return true;
}

